package com.gbase8c.dmt.db.meta;

import com.gbase8c.dmt.common.exception.MetadataException;
import com.gbase8c.dmt.model.migration.dto.DataSourceDto;
import com.gbase8c.dmt.service.DataSourceFactory;
import com.google.common.collect.Lists;
import com.speedment.common.tuple.Tuple2;
import com.speedment.common.tuple.Tuples;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import javax.sql.DataSource;
import java.util.Collection;
import java.util.List;

@Slf4j
public abstract class AbstractMeta implements Meta {

    protected DataSourceDto dataSourceDto;

    public AbstractMeta(DataSourceDto dataSourceDto) {
        this.dataSourceDto = dataSourceDto;
    }

    public DataSource dataSource() {
        return DataSourceFactory.getDataSource(dataSourceDto);
    }

    @Override
    public List<String> keywords() {
        return Lists.newArrayList();
    }

    private String args(int size) {
        String args = "";
        if (size > 0) {
            args = StringUtils.repeat("?", ",", size);
        }
        return args;
    }

    private Tuple2<Object[], Object[]> params(Object... params) {
        List<Object> replaces = Lists.newArrayList();
        List<Object> ps = Lists.newArrayList();
        if (ObjectUtils.isNotEmpty(params)) {
            for (Object param : params) {
                if (param instanceof Collection) {
                    Collection<?> collection = (Collection<?>) param;
                    int size = collection.size();
                    if (size > 0) {
                        replaces.add(args(collection.size()));
                        ps.addAll(collection);
                    } else {
                        replaces.add(args(1));
                        ps.add(null);
                    }
                } else {
                    ps.add(param);
                }
            }
        }
        return Tuples.of(CollectionUtils.isEmpty(replaces)? new Object[0] : replaces.toArray(),
                CollectionUtils.isEmpty(ps) ? new Object[0] : ps.toArray());
    }

    @Override
    public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) {
        QueryRunner qr = new QueryRunner(dataSource());
        try {
            Tuple2<Object[], Object[]> tuple2 = params(params);
            sql = String.format(sql, tuple2.get0());
            return qr.query(sql, rsh, tuple2.get1());
        } catch (Exception e) {
            log.error(String.format("query sql[%s] error!", sql), e);
            throw MetadataException.asMetadataException(String.format("query sql[%s] error!", sql), e);
        }
    }

    @Override
    public int execute(String sql, Object... params) {
        QueryRunner qr = new QueryRunner(dataSource());
        try {
            Tuple2<Object[], Object[]> tuple2 = params(params);
            sql = String.format(sql, tuple2.get0());
            return qr.execute(sql, tuple2.get1());
        } catch (Exception e) {
            log.error(String.format("execute sql[%s] error!", sql), e);
            throw MetadataException.asMetadataException(String.format("execute sql[%s] error!", sql), e);
        }
    }

}
